//
// Created by gogoqiu on 2022/3/23.
//

#include "BaseSettingsFrame.h"
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"

using namespace std;
using namespace rapidjson;
/**
 * test json build
 */

void testJsonBuild(){

}

//https://www.jianshu.com/p/f023cd83d59b
void AddStringArrayType(Document &d, Document::AllocatorType & allocator)
{
    Value valueStr(kStringType);      // 单个字符串
    Value valueStrArray(kArrayType);  // 字符串数组

    string strExtraSubject1 = "计算机";
    string strExtraSubject2 = "音乐";
    string strExtraSubject3 = "体育";

    valueStr.SetString(strExtraSubject1.c_str(), strExtraSubject1.size(), allocator);
    valueStrArray.PushBack(valueStr, allocator);

    valueStr.SetString(strExtraSubject2.c_str(), strExtraSubject2.size(), allocator);
    valueStrArray.PushBack(valueStr, allocator);

    valueStr.SetString(strExtraSubject3.c_str(), strExtraSubject3.size(), allocator);
    valueStrArray.PushBack(valueStr, allocator);

    d.AddMember("extraSubject", valueStrArray, allocator);
}

void ListItemPanel::UpdateExist(  ){
    //this->item = item;
    switch (item->getType()) {
        case PT_LIST: {
            auto *choice = (wxChoice *) valueControl;
            int select = choice->GetSelection();
            //
            if( select!=choiceData.chosen ){
                //wxArrayString strings = choice->GetStrings();
                //rebuild the json string
                //rebuild the item value
                choiceData.chosen = select;
                rapidjson::Document doc;
                rapidjson::Document::AllocatorType& allocator = doc.GetAllocator();
                doc.SetObject();

                rapidjson::Value chosen;
                chosen.SetInt( select );
                doc.AddMember( "chosen", chosen, doc.GetAllocator() );

                rapidjson::Value choices( rapidjson::kArrayType );
                //choices.SetArray();
                rapidjson::Value value( rapidjson::kStringType );
                const char* _s;
                wxString s;
                char buf[256];
                rapidjson::SizeType len;
                for( int i=0;i<choiceData.choices.size(); i++ ){
                    s = choiceData.choices.Item(i);
                    _s = s.operator const char *();
                    memset( buf, 0, 256 );
                    strcpy( buf , _s );
                    len = s.size();
                    value.SetString( buf, len, doc.GetAllocator() );
                    //itoa( i );
                    //value.SetString();
                    choices.PushBack( value, doc.GetAllocator() );
                }

                //Array array = choices.GetArray();
                /*
                for ( rapidjson::SizeType i = 0; i < choices.Size(); ++i) {
                    //temp = doc["chosen"];
                    const rapidjson::Value & temp1 = choices.operator[](i);
                    if( !temp1.IsString() ){
                        //return -5;
                    }
                    //choiceData.choices.push_back( _choices[i].GetString() );
                    _s = temp1.GetString();
                }*/
                //
                doc.AddMember( "choices", choices, doc.GetAllocator() );
                //json to string
                //doc.operator ParseResult();
                rapidjson::StringBuffer buffer;
                rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
                doc.Accept(writer);
                const char* json = buffer.GetString();
                item->setValue( json );
            }

            break;
        }
        case PT_SWITCH: {
            auto* checkBox = (wxCheckBox*)valueControl;
            if( item->getValue().IsSameAs( "true" ) ){
                checkBox->SetValue(true);
            }else{
                checkBox->SetValue(false);
            }
            break;
        }
        case PT_INPUT:{
            ((wxStaticText*)valueControl)->SetLabel( item->getValue() );
            break;
        }
    }
    //scroll
    //auto* frame = (BaseSettingsFrame*)m_parent;
    //use the frame to update the sqlite stored value
    //frame->UpdateItem( item );
    engine->UpdateItem( item->getKey(), item->getValue() );
}

//parameter: wxString& s
//item = new BaseItem( "keyFour", PT_LIST, wxT( "[ \"selection1\", \"|selection2|\", \"selection3\" ]" ),
//                         wxT( "[ \"selection1\", \"|selection2|\", \"selection3\" ]" ), "keyFour", "keyFour" );
/*
 * {\n  "ret": "101",\n  "error": [\n    {\n      "errortype": "A0001",\n      "errorstroke": {\n        "0": "0.2",\n        "1": "0.3"\n      }\n    },\n    {\n      "errortype": "A0021",\n      "errorstroke": {\n        "0": "0.2",\n        "1": "0.3"\n      }\n    }\n  ]\n}
 * {"ret":"101","error":[{"errortype":"A0001","errorstroke":{"0":"0.2","1":"0.3"}},{"errortype":"A0021","errorstroke":{"0":"0.2","1":"0.3"}}]}
 * { ret: 101,
 *   error: [ {"errortype":"A0001","errorstroke":{"0":"0.2","1":"0.3"}},
 *   {"errortype":"A0021","errorstroke":{"0":"0.2","1":"0.3"}} ]}
 *   https://json.im/jichu/cpp-json.html
 *   utf
 *   源代码
 *   utf8
 *  {
 *  chosen: 1,
 *  choices:[ "selection1", "selection2", "selection3" ]
 *  }
 *  wxArrayString& choices, wxString& selectedValue
 */
int ListItemPanel::parseSelections( const wxString& s ){
    //const char* ret =
    //        "{\"ret\":\"101\",\"error\":[{\"errortype\":\"A0001\",\"errorstroke\":{\"0\":\"0.2\",\"1\":\"0.3\"}},{\"errortype\":\"A0021\",\"errorstroke\":{\"0\":\"0.2\",\"1\":\"0.3\"}}]}";
    rapidjson::Document doc;
    const char* str = s.c_str().AsChar();
    //No viable conversion from 'rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>' to 'bool'
    //rapidjson::GenericDocument d =
    doc.Parse<rapidjson::kParseDefaultFlags>( str );
    if( doc.HasParseError()){
        rapidjson::ParseErrorCode code = doc.GetParseError();
        size_t offset = doc.GetErrorOffset();
        //rapidjson::ParseResult result( doc.GetParseError(), doc.GetErrorOffset() );
        //result.
        return -1;
    }
    if( !doc.HasMember("chosen") || !doc.HasMember("choices") ){
        return -2;
    }
    rapidjson::Value & temp = doc["chosen"];
    if( !temp.IsInt() ){
        return -3;
    }
    int chosen = doc["chosen"].GetInt();
    const rapidjson::Value & _choices = doc["choices"];
    if( !_choices.IsArray() ){
        return -4;
    }
    //const rapidjson::Value & temp1 = _choices[0];
    for ( rapidjson::SizeType i = 0; i < _choices.Size(); ++i) {
        //temp = doc["chosen"];
        const rapidjson::Value & temp1 = _choices.operator[](i);
        if( !temp1.IsString() ){
            return -5;
        }
        choiceData.choices.push_back( _choices[i].GetString() );
    }
    choiceData.chosen = chosen;
    //selectedValue = choiceData.choices.Item(chosen);
    return chosen;
}

void ListItemPanel::OnListClickItem(wxMouseEvent &event) {
    UpdateExist();
}

//
//
int BaseSettingsFrame::loadAll() {
    wxMessageBox( wxT("loadAll放弃使用"), wxT("已放弃"), wxICON_ERROR );
    return -1;
    int result = this->engine->FillItems(& ( this->items ) );
    return result;
}

BaseSettingsFrame::BaseSettingsFrame( wxWindow *parent, const wxString& appId ):wxFrame(
        parent, wxID_ANY, "配置", wxDefaultPosition, wxDefaultSize,
        wxDEFAULT_FRAME_STYLE ^ wxMAXIMIZE_BOX ^ wxRESIZE_BORDER ) {
    //
    CreateStatusBar();
    SetStatusText("Welcome to wxWidgets!");
    this->appId = appId;
    //
    engine = new SqliteQueryEngine( appId );
}

//需要实用，继承类中具体实现
int BaseSettingsFrame::setup() {
    return 0;
}

/*
 * use connect to setup the click callback to the panel item
 */
int BaseSettingsFrame::UpdateItem(const wxString &key, const wxString &value) {
    //update the control value
    //ListItemPanel *control = FindControl( key );
    //update the core data value
    /*
    for( auto * item: items ){

    }*/
    engine->UpdateItem( key, value );
    return 0;
}

int BaseSettingsFrame::UpdateItem( BaseItem * item ){
    UpdateItem( item->getKey(), item->getValue() );
}

ListItemPanel *BaseSettingsFrame::FindControl( const wxString& key ) {
    /*
    for( auto * item: items ) {
        if( item->getKey().IsSameAs( key )){

        }
    }*/
    for( auto *control: controls ){
        if( control->GetName().IsSameAs(key) ){
            return control;
            break;
        }
    }
    return nullptr;
}

/*
 * wxScrolledWindow:
 *
 */
void BaseSettingsFrame::BuildView() {
    //auto *sizer = new wxBoxSizer( wxHORIZONTAL );
    auto *sizer = new wxBoxSizer( wxVERTICAL );
    //auto* panel = new wxPanel( this );
    //sizer->Add( panel, 1, wxEXPAND, 0 );
    //this->SetSizer( sizer );

    m_listbox = new wxScrolledWindow(this, wxID_ANY,
                                     wxDefaultPosition, wxDefaultSize, wxHSCROLL | wxVSCROLL );
    //m_listbox = new wxScrolledWindow(this);
    m_listbox->SetScrollRate(5, 5 );
    m_listbox->Scroll(50, 10);
    //sizer->Add( m_listbox, 0, wxEXPAND|wxALL, 15 );
    ListItemPanel* panel1;
    //MyObj *object;
    //wxHORIZONTAL
    auto *panelRootBoxSizer = new wxBoxSizer(wxVERTICAL);
    //sizer->Add( panelRootBoxSizer, 0, wxEXPAND|wxALL, 15 );
    for( auto* item:items ) {
        //new ListItemPanel( this, item );
        //innerSizer->Fit( m_panel );
        panel1 = new ListItemPanel( m_listbox, engine, item );
        //
        panelRootBoxSizer->Add( panel1, 0, wxEXPAND|wxALL, 15);
        //controls.push_back(panel1);
        /*
        object = new MyObj( item );
        panel1->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler( BaseSettingsFrame::OnClickItem ),
                         object, this );
                         */
    }
    m_listbox->SetSizer( panelRootBoxSizer );
    m_listbox->Layout();
    //SetSizer( sizer );
    //Centre();
}

void BaseSettingsFrame::OnClickItem(wxMouseEvent& event) {

}

/*
 * load all item from sqlite
 * if not found, set the default value
 * */
/*
void BaseSettingsFrame::collectAllKeys() {

}
*/

/**
 *
 */
 /*
void BaseSettingsFrame::buildView() {

}
*/